﻿using Moq;
using Sustainalytics.DataLayer;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Replication.Publisher;
using Sustainalytics.ScreeningTool.BL.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace Sustainalytics.ScreeningTool.BL.Tests
{
    public class FilterManagerTests
    {
        private readonly Dictionary<string, string> serviceBusParams = new Dictionary<string, string>
        {
            { "ConnectionString", "Endpoint=sb://sust-test-ns.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Dh/4Cq0qrOC7xOJFUYHLGJvQ6pqj4BuwjTS4HUjL6uQ=" },
            { "RegionEndpoint", "TT" },
            { "SessionId", "Screening" },
            { "TopicPath", "test-topic" },
        };

        private readonly ServiceBusPublisher _serviceBusPublisher;

        private readonly List<Filter> mockFilters = new List<Filter>
        {
            new Filter {
                Id = new Guid("86D0B8C9-136B-4B9A-B761-D02E0D3F599E"),
                Name="shared from account not owned by current user",
                Type=FilterType.Standard,
                OwnerUserId = new Guid("38DA5AC4-1705-4229-8099-C239A9EA05C2"),
                AccountId = new Guid("11111111-0000-0000-0000-000000000000"),
                IsFavorite= false,
                Status = FilterStatus.Active,
                TargetType= TargetType.Screening
            },
            new Filter {
                Id = new Guid("C7E964A0-9246-46C4-B895-19C9221C211A"),
                Name="shared from account not owned by current user",
                Type=FilterType.Shared,
                OwnerUserId = new Guid("4C9AF559-5D2F-4A6C-AD90-DEC585D002C4"),
                AccountId = new Guid("11111111-0000-0000-0000-000000000000"),
                IsFavorite= true,
                Status = FilterStatus.Active,
                TargetType= TargetType.Screening
            },
            new Filter {
                Id = new Guid("8B076072-9A18-4728-80E9-4B9236D19E91"),
                Name="my custom filter 2 fav",
                Type=FilterType.Private,
                OwnerUserId = new Guid("11111111-1111-0000-0000-000000000000"),
                AccountId = new Guid("11111111-0000-0000-0000-000000000000"),
                IsFavorite= true,
                Status = FilterStatus.Active,
                TargetType= TargetType.Screening
            },
            new Filter {
                Id = new Guid("6908FFCF-0530-4115-91C4-52D01CF2E48C"),
                Name="my custom filter 1",
                Type=FilterType.Private,
                OwnerUserId = new Guid("11111111-1111-0000-0000-000000000000"),
                AccountId = new Guid("11111111-0000-0000-0000-000000000000"),
                IsFavorite= false,
                Status = FilterStatus.Active,
                TargetType= TargetType.Screening
            }
        };

        private readonly User mockCurrentUser = new User
        {
            Id = new Guid("11111111-1111-0000-0000-000000000000"),
            AccountId = new Guid("11111111-0000-0000-0000-000000000000"),
            Email = "testuser@sustainalytics.com"
        };

        public FilterManagerTests()
        {
            _serviceBusPublisher = new ServiceBusPublisher(serviceBusParams);
        }

        [Fact]
        public void GetUserFiltersReturnsAllFilters()
        {
            //Arrange
            var mockIAccountsRepository = new Mock<IAccountsRepository>();
            mockIAccountsRepository.Setup(x => x.GetUserByUsername(mockCurrentUser.Email))
                .Returns(mockCurrentUser);

            //no mock for companyScreeningRepository yet, use null

            var mockIFiltersRepository = new Mock<IFiltersRepository>();

            //mock standard filters
            mockIFiltersRepository.Setup(x => x.GetStandardFilters(TargetType.Screening))
                .Returns(mockFilters.Where(x => x.Type == FilterType.Standard).ToList());

            //mock shared filters
            mockIFiltersRepository.Setup(x => x.GetFiltersByAccountId(mockCurrentUser.Id, mockCurrentUser.AccountId.Value, TargetType.Screening, FilterType.Shared))
                .Returns(mockFilters.Where(x => x.OwnerUserId != mockCurrentUser.Id && x.AccountId == mockCurrentUser.AccountId && x.Type == FilterType.Shared).ToList());

            //mock owner filters
            mockIFiltersRepository.Setup(x => x.GetFiltersByOwner(new List<Guid> { mockCurrentUser.Id }, mockCurrentUser.AccountId, TargetType.Screening))
                .Returns(mockFilters.Where(x => x.OwnerUserId == mockCurrentUser.Id && x.AccountId == mockCurrentUser.AccountId).ToList());

            //no mock for clientDWRepository yet, use null

            var mockUserFilterSettingsRepository = new Mock<IRepositoryCollection<UserFilterSettings>>();

            var filterManager = new FilterManager(mockIAccountsRepository.Object, null, mockIFiltersRepository.Object, null, mockUserFilterSettingsRepository.Object, null, _serviceBusPublisher);

            //Act
            var actualFilters = filterManager.GetUserFilters(mockCurrentUser.Email, TargetType.Screening);

            //Assert
            Assert.Equal(mockFilters.Count, actualFilters.Count);
            Assert.True(mockFilters.SequenceEqual(actualFilters));
        }

        [Fact]
        public void LoadFilterReturnsExpectedFilter()
        {
            //Assert

            var filterId = new Guid("6908FFCF-0530-4115-91C4-52D01CF2E48C");
            var expectedResult = mockFilters.Find(x => x.Id == filterId);

            var mockIAccountsRepository = new Mock<IAccountsRepository>();
            mockIAccountsRepository.Setup(x => x.GetUserByUsername(mockCurrentUser.Email))
                .Returns(mockCurrentUser);

            //no mock for companyScreeningRepository yet, use null

            var mockIFiltersRepository = new Mock<IFiltersRepository>();
            mockIFiltersRepository.Setup(x => x.GetFilterById(filterId))
                .Returns(expectedResult);

            //no mock for clientDWRepository yet, use null

            var mockUserFilterSettingsRepository = new Mock<IRepositoryCollection<UserFilterSettings>>();

            var filterManager = new FilterManager(mockIAccountsRepository.Object, null, mockIFiltersRepository.Object, null, mockUserFilterSettingsRepository.Object, null, _serviceBusPublisher);

            //Act
            var actualResult = filterManager.LoadFilter(filterId, mockCurrentUser.Email);

            //Assert
            Assert.NotNull(actualResult);
            Assert.Equal(expectedResult, actualResult);
            Assert.Equal(expectedResult.Id, actualResult.Id);
        }

        [Fact]
        public void SaveFilterReturnsExpectedFilter()
        {
            var postedObject = new FilterAddEditDto
            {
                Name = "test create new filter",
                Type = FilterType.Private,
                OwnerUserId = new Guid("11111111-1111-0000-0000-000000000000"),
                AccountId = new Guid("11111111-0000-0000-0000-000000000000"),
                IsFavorite = false,
                Status = FilterStatus.Active,
                TargetType = TargetType.Screening
            };

            var mockIAccountsRepository = new Mock<IAccountsRepository>();
            mockIAccountsRepository.Setup(x => x.GetUserByUsername(mockCurrentUser.Email))
                .Returns(mockCurrentUser);

            var mockIFiltersRepository = new Mock<IFiltersRepository>();
            mockIFiltersRepository.Setup(x => x.SaveFilter(It.IsAny<Filter>()))
                .Returns(true);

            var mockUserFilterSettingsRepository = new Mock<IRepositoryCollection<UserFilterSettings>>();

            var filterManager = new FilterManager(mockIAccountsRepository.Object, null, mockIFiltersRepository.Object, null, mockUserFilterSettingsRepository.Object, null, _serviceBusPublisher);

            var actualResult = filterManager.SaveFilter(mockCurrentUser.Email, postedObject);

            //Assert
            Assert.NotNull(actualResult);
            Assert.Equal(postedObject.Name, actualResult.Name);
            Assert.Equal(postedObject.OwnerUserId, actualResult.OwnerUserId);
            Assert.Equal(postedObject.TargetType, actualResult.TargetType);
        }
    }
}
